# coding=utf-8
from datetime import datetime
import io
import sys
import json
import requests
import threading
from markdown import markdown
from docx import Document
from docx.shared import Pt
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import json_repair
import uuid
from docx.shared import Pt, RGBColor
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from bid.generate.tools.dawate import main_stream

from bid.generate.models import ExBidCatalog, ExBid

def add_m_heading(document, text, level):
    """添加标题，根据级别设置样式"""
    heading = document.add_heading(level=level)
    run = heading.add_run(text)

    # 设置字体
    run.font.name = '宋体'
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    run.font.size = Pt(16)
    run.font.bold = True #加粗
    _set_font_color(run)  # 设置字体颜色为黑色
    run.font.italic = False #设置非斜体
    if level==1:
        heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #设置为居中对齐
def add_heading(document, text, level):
    """添加标题，根据级别设置样式"""
    heading = document.add_heading(level=level)
    run = heading.add_run(text)

    # 设置字体
    run.font.name = '宋体'
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
    _set_font_size(run, level)
    run.font.bold = True #加粗
    _set_font_color(run)  # 设置字体颜色为黑色
    run.font.italic = False #设置非斜体
    if level==0:
        heading.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #设置为居中对齐
def _set_font_size(run, level):
    """根据标题级别设置字体大小"""
    size_mapping = {1: Pt(15), 2: Pt(14), 3: Pt(14), 4: Pt(12)}
    run.font.size = size_mapping.get(level, Pt(12))  # 默认为小四

def _set_font_color(run):
    """设置字体颜色为黑色"""
    run.font.color.rgb = RGBColor(0, 0, 0)  # 黑色的RGB值为(0, 0, 0)
def add_content(document, text):
    """添加内容"""
    paragraph = document.add_paragraph(text)
    paragraph.paragraph_format.first_line_indent = Pt(24)  # 首行缩进
    set_font(paragraph, '宋体', 10.5, 1.5)  # 设置字体为宋体、5号字、1.5倍行间距
    paragraph.paragraph_format.space_before = Pt(0)  # 段前间距设置为0
    paragraph.paragraph_format.space_after = Pt(0)  # 段后间距设置为0
    paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.JUSTIFY
def set_font(paragraph, font_name, font_size, line_spacing):
    """设置字体和行间距"""
    run = paragraph.runs[0]
    run.font.name = font_name
    run._element.rPr.rFonts.set(qn('w:eastAsia'), font_name)
    run.font.size = Pt(font_size)
    paragraph.paragraph_format.line_spacing = line_spacing


global_thread = {}

# 大模型线程并发数量
pool_sema = threading.BoundedSemaphore(20)  # 或使用Semaphore方法

from docx import Document


def list_styles(docx_path):
    document = Document(docx_path)
    styles_set = set()  # 使用集合来避免重复的样式名称
    for paragraph in document.paragraphs:
        if paragraph.style:
            print("paragraph.style.name:", paragraph.style.name)
            styles_set.add(paragraph.style.name)
        else:
            print("paragraph:", paragraph)

    return styles_set


from docx import Document


def read_headings(doc_path):
    # 加载文档
    doc = Document(doc_path)
    # 用于存储标题层级的字典
    headings_dict = {}
    # 用于追踪当前标题的父标题的栈
    stack = []

    # 遍历文档中的所有段落


    current_dict = None
    for index,para in enumerate(doc.paragraphs):#doc.paragraphs list
        # 获取段落的样式名称
        style_name = para.style.name
        # 检查样式名称是否以"Heading"开头
        if style_name.startswith("Heading"):
            # 获取标题层级
            level = int(style_name.split(" ")[1])
            # 获取标题文本
            text = para.text
            titl_text=text
            # 创建当前标题的字典
            # current_dict = {text: []}
            if 'Heading'  in doc.paragraphs[index+1].style.name:
                current_dict = {text: {"text": "", "child": []}}
                # current_dict = {text: []}
            else:
                #后面有内容的
                current_dict = {text+'*': {"text": [], "child": []}}
                # current_dict = {text+'*': []}

            if level == 2:
                # Heading 2 作为顶层键
                headings_dict.update(current_dict)
                stack = [current_dict]
            else:
                # 更深层级的标题，需要找到对应的父层级
                # 确保栈的长度与当前层级匹配
                while len(stack) > level - 2:
                    stack.pop()

                # 当前标题的父标题
                parent_dict = stack[-1]
                # 获取父标题的键
                parent_key = list(parent_dict.keys())[0]

                # 如果父标题下还没有子标题列表，则创建一个
                if parent_key not in parent_dict:
                    parent_dict[parent_key] = []

                # 将当前标题添加到父标题的字典中
                # safe_print( parent_dict)
                # safe_print( parent_key)
                # safe_print(current_dict)
                # parent_dict[parent_key].append(current_dict)
                parent_dict[parent_key]['child'].append(current_dict)

                # 更新栈
                stack.append(current_dict)
        else:
            text = para.text
            if current_dict:
                current_dict[list(current_dict.keys())[0]]['text'].append(text)


    return headings_dict


# 示例使用
# doc_path = "./招投标文件生成(data)/24_技术要求_标的1：业务系统2024年第五批采购项目-标.docx"  # 替换为你的文档路径
# 全局变量

def dify_chat_streaming(query, number):
    # API端点和API密钥
    # url = 'http://172.16.32.45:11206/v1/chat-messages' #v1/chat-messages'调用聊天框  workflows/run智能体
    # url = 'http://192.168.73.129:80/v1/chat-messages'  # v1/chat-messages'调用聊天框  workflows/run智能体
    # api_key = 'app-yDbyAnwoY4KjzX84qGtQMyz6'  # 'app-ldimOhba65Dq5BlHpPXQaDka'  # 替换为您的实际API密钥
    url = 'http://192.168.43.12:8083/v1/chat-messages'  # v1/chat-messages'调用聊天框  workflows/run智能体
    api_key = 'app-E5OKzE323dg4s4g8T8lwmG40'  # 'app-ldimOhba65Dq5BlHpPXQaDka'  # 替换为您的实际API密钥

    # 请求头
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Content-Type': 'application/json'
    }

    # 请求数据
    data = {
        "inputs": {},
        "query": query,
        "response_mode": "streaming",  # blocking streaming
        "conversation_id": "",
        "user": "abc-123",
    }

    # 将数据转换为JSON字符串
    data_json = json.dumps(data)

    # 发送POST请求
    # response = requests.post(url, headers=headers, data=data_json.encode("utf-8"))
    # response.encoding = 'utf-8'

    answer = ''

    # 发送GET请求，并设置stream=True
    with requests.post(url, headers=headers, data=data_json.encode("utf-8"), stream=True) as r:
        # 检查请求是否成功
        r.raise_for_status()

        # 打印每一行文本
        for line in r.iter_lines():
            # 忽略保持连接的空行
            if line:
                # 解码每一行，因为返回的数据可能是字节串
                line = line.decode('utf-8')
                if 'data: ' in line:
                    data = json.loads(line.replace('data: ', ''))
                    if data['event'] == 'message':
                        # print(data['answer'])
                        answer += data['answer']
    # global_thread[query] = answer
    global_thread[number] = {query: answer}
    return answer


import os
import re


def json_process(data_answ):
    pattern = r'```json(.*?)```'
    matches = re.findall(pattern, data_answ, re.DOTALL)  # 抽取出内容并判断长度
    if matches:
        c_statement = json_repair.loads(matches[-1])
        # print("\n-------step2查到的对应的内容为：\n", c_statement)
    else:
        c_statement = {}
    # decoded_object = json_repair.loads(c_statement)
    # return json.dumps(decoded_object,ensure_ascii=False)
    return c_statement

def markdown_process(data_answ):
    pattern = r'```markdown(.*?)```'
    matches = re.findall(pattern, data_answ, re.DOTALL)  # 抽取出内容并判断长度
    if matches:
        c_statement = matches[-1]
        # print("\n-------step2查到的对应的内容为：\n", c_statement)
    else:
        c_statement = ''
    # decoded_object = json_repair.loads(c_statement)
    # return json.dumps(decoded_object,ensure_ascii=False)
    return c_statement


def numbering_titles(title_dict, text=[],prefix=''):
    """
    Recursively number the titles in the given dictionary according to their hierarchy.

    :param title_dict: Dictionary or list containing the titles structured hierarchically.
    :param prefix: The current prefix for numbering, used in recursion.
    :return: A new dictionary or list with numbered titles.
    """
    if isinstance(title_dict, dict):
        numbered_dict = {}
        for index, (key, value) in enumerate(title_dict.items(), start=1):
            new_prefix = f"{prefix}{index}." if prefix else f"{index}."
            numbered_dict[f"{new_prefix} {key}"] = numbering_titles(value['child'], value['text'], new_prefix)
        return numbered_dict
    elif isinstance(title_dict, list):
        numbered_list = []
        for index, item in enumerate(title_dict, start=1):
            if isinstance(item, dict):
                for key, value in item.items():
                    new_prefix = f"{prefix}{index}." if prefix else f"{index}."
                    numbered_list.append({f"{new_prefix} {key}": numbering_titles(value['child'], value['text'], new_prefix)})
            else:
                numbered_list.append(item)
        return {"text":text, "child":numbered_list}
    else:
        return title_dict

#将树结构整理为数据库存储格式
def build_tree(data, bid, level=1, parent_id=[], order={}):

    #列表长度大于0时
    if len(data) > 0:
        #迭代列表元素
        for data_dict in data:
            for key, value in data_dict.items():
                print(value)
                #原文内容
                content = '\n'.join(value['text'])
                #子节点
                value = value['child']

                # 添加该层级标题顺序
                if str(parent_id[:level - 1]) not in order:
                    order[str(parent_id[:level - 1])] = 0
                else:
                    order[str(parent_id[:level - 1])] += 1

                #以*结尾，则为需要生成内容
                if key.endswith('*'):
                    key = key[:-1]
                    generate_flag = '1'
                else:
                    generate_flag = '0'

                #存入数据库
                #路径
                path = ','.join(parent_id[:level - 1])
                #uuid
                if path:
                    catalog_uuid = str(uuid.uuid4()).replace('-','')
                else:
                    catalog_uuid = 'root'

                bid_catalog = ExBidCatalog.objects.create(uuid=catalog_uuid,
                                                          bid=bid,
                                                          type='tech',
                                                          name=key,
                                                          generateFlag=generate_flag,
                                                          originalContent=content,
                                                          path=path,
                                                          order=order[str(parent_id[:level - 1])])

                #级别序号区别开
                parent_id = parent_id[:level-1] + [str(bid_catalog.id)]
                # path = ','.join(parent_id)

                # print(path,order[str(parent_id[:level - 1])], key)

                #传入父路径
                build_tree(value, bid, level+1, parent_id, order)

#生成目录结构
def tech_catalog_generate(doc_path, _id):

    headings = read_headings(doc_path)
    # 原型界面设计 方案
    top_title = headings['技术实现方案']['child'][6]
    numbered_title_dict = numbering_titles(top_title)

    #如果目录是字典，则转为列表
    if isinstance(top_title, dict):
        title_dict = [{'目录':{'text': [], 'child':[numbered_title_dict]}}]
    else:
        title_dict = [{'目录': {'text': [], 'child':numbered_title_dict['child']}}]

    #获取bid的对象
    bid = ExBid.objects.filter(id=_id).first()

    #存入数据库
    build_tree(title_dict, bid)

    #整理为前端展示的目录结构，并添加uuid
    # print(json.dumps(title_dict, ensure_ascii=False))
    #
    #
    # print(1)

#生成目录下的内容（子）
def tech_catelog_content_generate(title, _id, config_params):
    global pool_sema
    # 开启线程限制
    with pool_sema:
        # url = 'http://192.168.43.12:8083/v1/chat-messages'  # v1/chat-messages'调用聊天框  workflows/run智能体
        # api_key = 'app-E5OKzE323dg4s4g8T8lwmG40'  # 'app-ldimOhba65Dq5BlHpPXQaDka'  # 替换为您的实际API密钥
        #
        # # 请求头
        # headers = {
        #     'Authorization': f'Bearer {api_key}',
        #     'Content-Type': 'application/json'
        # }

        #提示词
        prompt = f"根据知识库中查找的相关内容，在原始内容的基础上进行内容扩写并概括总结润色，扩写的内容只需要一段。如果知识库内容不存在，" \
                 f"请结合相关内容进行内容补充与完善，内容重复不影响，输出结果中不要出现“原文内容”“扩写”“根据xxx内容”“技术规范书”等的字样，" \
                 f"生成的内容需要和标题及原文强相关"

        title = f"{prompt}\n{title}"

        # # 请求数据
        # data = {
        #     "inputs": {},
        #     "query": title,
        #     "response_mode": "streaming",  # blocking streaming
        #     "conversation_id": "",
        #     "user": "abc-123",
        # }
        #
        # # 将数据转换为JSON字符串
        # data_json = json.dumps(data)
        #
        # answer = ''
        #
        # # 发送GET请求，并设置stream=True
        # with requests.post(url, headers=headers, data=data_json.encode("utf-8"), stream=True) as r:
        #     # 检查请求是否成功
        #     r.raise_for_status()
        #
        #     # 打印每一行文本
        #     for line in r.iter_lines():
        #
        #         # 忽略保持连接的空行
        #         if line:
        #             # 解码每一行，因为返回的数据可能是字节串
        #             line = line.decode('utf-8')
        #             if 'data: ' in line:
        #                 data = json.loads(line.replace('data: ', ''))
        #                 if data['event'] == 'message':
        #                     # print(data['answer'])
        #                     answer += data['answer']
        #                     print(data['answer'],end='')
        #                 elif data['event'] == 'message_end':
        #                     break

        config_params['messages'] = [{"role": "user", "content": title}]
        answer = ''
        print('输入数据：', title)
        response_content = main_stream(config_params)
        try:
            while True:
                a = next(response_content)
                answer += a
                # sys.stdout.write(f"\r{[a]}")
                # sys.stdout.flush()
                print(a, end='')
        except StopIteration:
            pass

        #去除思考过程
        answer = answer.split('</think>')[-1]

        if answer:
            #存入数据库
            ExBidCatalog.objects.filter(id=_id).update(genarateContent=f"{answer}\n附原型图如下所示：")
        else:
            # 存入数据库
            ExBidCatalog.objects.filter(id=_id).update(genarateContent='生成失败')

#生成目录下的内容
def tech_generate(catalogs, config_params):
    # 定义线程池
    threadpool = []

    #记录id对应的标题
    id2title = {}
    print('===========================')

    # 提取基础信息
    for catalog in catalogs:

        # 记录id对应的标题
        id2title[str(catalog['id'])] = catalog['name']

        #如果不需要生成，则跳过
        if catalog['generateFlag'] == '0':
            continue

        #将路径标题和自身标题结合，不要目录
        path_split = catalog['path'].split(',')[1:]
        path_split.append(str(catalog['id']))
        query = {"标题":','.join([id2title[_id] for _id in path_split]), "原文":catalog['originalContent']}
        query = json.dumps(query, ensure_ascii=False)

        # print(query)

        threadpool.append(threading.Thread(target=tech_catelog_content_generate, args=(query,catalog['id'], config_params)))

        # print([catalog['id'], query])

    # 启用和阻塞线程
    for th in threadpool:
        th.start()
    for th in threadpool:
        threading.Thread.join(th)

    print(f'技术文件目录内容生成完成======================')

#将数据库的内容转为word
def tree2word(catalogs, doc = None):

    if doc is None:
        # 保存到文件中
        doc = Document()
        add_m_heading(doc, '(编号)技术要求', 1)

    if len(catalogs) > 0:
        for catalog in catalogs:

            #添加标题
            title_level = len(catalog['path'].split(','))
            add_heading(doc, catalog["title"], title_level)

            #如果有生成内容添加内容
            if catalog['content']:
                #通过\n切分，每段单独放置
                paras = catalog['content'].split('\n')
                for para in paras:
                    if para:
                        add_content(doc, para)

            #迭代
            doc = tree2word(catalog['children'].values(), doc)

    return doc

def docx_title_structure(doc_path):
    current_time = datetime.now()
    f_n = current_time.strftime('%Y%m%d_%H%M%S')

    # f = f'./out_json/{f_n}.html'
    if not os.path.exists('./out_json'):
        os.mkdir('./out_json')
    # file = open(f, 'w', encoding='utf-8')
    headings = read_headings(doc_path)
    print(headings)
    # 原型界面设计 方案
    number = 0
    basic_info_threadpool = []
    # top_title = headings['技术实现方案'][4:7]
    top_title = headings['技术实现方案']['child'][4:7]
    numbered_title_dict = numbering_titles(top_title)

    # 如果目录是字典，则转为列表
    if isinstance(top_title, dict):
        numbered_title_dict = [numbered_title_dict]
    else:
        numbered_title_dict = numbered_title_dict['child']

    tnp = []
    for title_dict in numbered_title_dict:
        for keys, values in title_dict.items():
            print('keys', keys)
            # file.writelines(f'{keys}\n')
            for v_i in values:
                for k_i, v_i in v_i.items():
                    # file.writelines(f'{k_i}\n')
                    for v_j in v_i:
                        for k_ii, v_ii in v_j.items():
                            # file.writelines(f'{k_ii}\n')
                            # print(k_ii,v_ii)
                            for v_jj in v_ii:
                                for k_iii, v_iii in v_jj.items():
                                    # print('v_iii:',v_iii)
                                    # file.writelines(f'{k_iii}\n')
                                    if v_iii:
                                        print('最终结果', keys, k_i, k_ii, k_iii)
                                        _s = [keys, k_i, k_ii, k_iii]
                                        for _ind, _list_tem in enumerate(_s):
                                            if _list_tem.endswith('*'):
                                                # print(s[0:_ind+1])
                                                query = ','.join(_s[0:_ind + 1]).replace('*', '')
                                                if query not in tnp:
                                                    number += 1
                                                    # basic_info_threadpool.append(
                                                    #     threading.Thread(target=dify_chat_streaming, args=(query, number)))
                                                    tnp.append(query)
                                                    print('============================',query)
                                                    global_thread[number] = {query: '</details>你是谁'}

                                        for v_jjj in v_iii:
                                            for k_iiii, v_iiii in v_jjj.items():
                                                _s = [keys, k_i, k_ii, k_iii, k_iiii]
                                                for _ind, _list_tem in enumerate(_s):
                                                    if _list_tem.endswith('*'):
                                                        # print(s[0:_ind+1])
                                                        query = ','.join(_s[0:_ind + 1]).replace('*', '')
                                                        # print(query)
                                                        if query not in tnp:
                                                            number += 1
                                                            # basic_info_threadpool.append(
                                                                # threading.Thread(target=dify_chat_streaming,
                                                                #                  args=(query, number)))
                                                            tnp.append(query)
                                                            print('============================',query)
                                                            global_thread[number] = {query: '</details>你是谁'}
                                    #
                                    else:
                                        _s = [keys, k_i, k_ii, k_iii]
                                        for _ind, _list_tem in enumerate(_s):
                                            if _list_tem.endswith('*'):
                                                # print(s[0:_ind+1])
                                                query = ','.join(_s[0:_ind + 1]).replace('*', '')
                                                print('============================',query)
                                                if query not in tnp:
                                                    number += 1
                                                    # basic_info_threadpool.append(
                                                        # threading.Thread(target=dify_chat_streaming, args=(query, number)))
                                                    tnp.append(query)
                                                    print(query)
                                                    global_thread[number] = {query: '</details>你是谁'}

        # 整一个线程池
        # 启用和阻塞线程
    for th in basic_info_threadpool:
        th.start()
    for th in basic_info_threadpool:
        threading.Thread.join(th)

    # 保存到文件中
    doc = Document()
    # 保存到文件中
    dict_new = {}
    global_thread_sorted = sorted(global_thread.keys())
    out_print = {key: global_thread[key] for key in global_thread_sorted}

    # with open('测试json.json', 'w') as f:
    #     json.dump(out_print, f, ensure_ascii=False)

    # for num_keys, values in out_print.items():
    #     for k_i, v_i in values.items():
    #         for index, k_ii in enumerate(k_i.split(',')):
    #             print(index)
    #             if index + 1 not in dict_new.keys():
    #                 add_heading(doc, k_ii, index + 1)
    #             else:
    #                 if k_ii not in dict_new[index + 1]:
    #                     add_heading(doc, k_ii, index + 1)
    #             if index + 1 not in dict_new.keys():
    #                 dict_new[index + 1] = []
    #                 dict_new[index + 1].append(k_ii)
    #             else:
    #                 # print('dict_new',dict_new)
    #                 if k_ii not in dict_new[index + 1]:
    #                     dict_new[index + 1].append(k_ii)
    #         s = v_i.split('</details>')[-1]  # +"\t好东西"
    #         if '```json' in s:
    #             s_new = json_process(s)
    #             d_cc = json_repair.loads(s_new)
    #             # file.writelines(f'{s_new}\n')
    #             add_content(doc, d_cc['原文内容'])
    #             [add_content(doc, i) for i in d_cc['扩写内容'].split('\n') if i]
    #             # add_content(doc, d_cc['扩写内容'])
    #             add_content(doc, '附原型图如下所示：')
    #
    #         else:
    #             if '服务走丢了，请稍后再来' in s:
    #                 print("error", s)
    #             #     basic_info_threadpool.append(threading.Thread(target=dify_chat_streaming, args=(k_i, number)))
    #             #     tnp.append(query)
    #             # file.writelines(f'{s}\n')
    #             else:
    #                 d_cc = json_repair.loads(s)
    #                 if isinstance(d_cc, dict):
    #                     if '原文内容' in d_cc.keys():
    #                         add_content(doc, d_cc['原文内容'])
    #                         [add_content(doc, i) for i in d_cc['扩写内容'].split('\n') if i]
    #                         # add_content(doc, d_cc['扩写内容'])
    #                         add_content(doc, '附原型图如下所示：')
    #             # print(f'error:{s}')
    #             # add_content(doc, s)
    #             # add_content(doc, '附原型图如下所示：')
    # doc.save(f'./out_json/output_{f_n}.docx')
    # print("HTML文件已保存为./out_json/output_{f_n}.docx")

    # 获取结果
    # 启用线程

    # 合并结果
    # 合并基础信息列表


if __name__ == "__main__":
    # 获取文档中的所有样式名称
    # docx_path='./data/24_技术要求_标的1：业务系统2024年第五批采购项目-标.docx'
    # styles_used = list_styles(docx_path)  # 自动获取样式

    # 示例使用
    doc_path = "/media/977GB/virtualbox/24_技术要求_标的1：业务系统2024年第五批采购项目-标.docx"  # 替换为你的文档路径
    docx_title_structure(doc_path)
    # tech_catalog_generate(doc_path)

    # query=''
    # data_answ = dify_chat_streaming(str((query)))